package com.szjz.elasticsearch.eshelper.wrapper;


import com.szjz.elasticsearch.eshelper.base.PropertyNamer;
import com.szjz.elasticsearch.eshelper.functionainterface.ESFunction;
import com.szjz.elasticsearch.eshelper.util.LambdaUtils;
import com.szjz.elasticsearch.eshelper.util.SerializedLambda;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 史博
 * @desc ES查询包装类
 * @date 2021/1/17 下午4:23 周日
 */
@Slf4j
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class ESQueryWrapper<T> implements ConditionWrapper<T, ESQueryWrapper<T>>,
        OrderWrapper<T, ESQueryWrapper<T>>,
        SourceWrapper<T, ESQueryWrapper<T>> {

    /**
     * 保存条件集
     */
    public List<Condition> conditions = new ArrayList<>();


    /**
     * or
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> or(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】or property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.OR).column(propertyName).value(val).build());
        return this;
    }

    /**
     * or LIKE '%值'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> orLikeLeft(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】orLikeLeft property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.OR_LIKE_LEFT).column(propertyName).value(val).build());
        return this;
    }

    /**
     * or LIKE '值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> orLikeRight(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】orLikeRight property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.OR_LIKE_RIGHT).column(propertyName).value(val).build());
        return this;
    }

    /**
     * or or LIKE '%值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> orLike(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】orLike property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.OR_LIKE).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> eq(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】eq property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.EQ).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> neq(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】neq property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.NEQ).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> gt(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】gt property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.GT).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> lt(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】lt property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LT).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> gte(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】gte property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.GTE).column(propertyName).value(val).build());
        return this;
    }

    @Override
    public ESQueryWrapper<T> lte(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】lte property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LTE).column(propertyName).value(val).build());
        return this;
    }

    /**
     * 范围
     *
     * @param column 字段
     * @param values 值
     * @return
     */
    @Override
    public ESQueryWrapper<T> in(ESFunction<T, ?> column, List<?> values) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】in property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.IN).column(propertyName).values(values).build());
        return this;
    }

    /**
     * is null
     *
     * @param column 字段
     * @return
     */
    @Override
    public ESQueryWrapper<T> isNull(ESFunction<T, ?> column) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】in property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.IS_NULL).column(propertyName).value(null).build());
        return this;
    }

    /**
     * is not null
     *
     * @param column 字段
     * @return
     */
    @Override
    public ESQueryWrapper<T> isNotNull(ESFunction<T, ?> column) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】in property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.IS_NOT_NULL).column(propertyName).value(null).build());
        return this;
    }

    /**
     * LIKE '%值'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> likeLeft(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】likeLeft property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LIKE_LEFT).column(propertyName).value(val).build());
        return this;
    }

    /**
     * LIKE '值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> likeRight(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】likeRight property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LIKE_RIGHT).column(propertyName).value(val).build());
        return this;
    }

    /**
     * LIKE '%值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> like(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】like property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LIKE).column(propertyName).value(val).build());
        return this;
    }

    /**
     * LIKE '?值'
     * 只能匹配左边一个字符
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> likeLeftOne(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】like property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LIKE_LEFT_ONE).column(propertyName).value(val).build());
        return this;
    }

    /**
     * LIKE '值?'
     * 只能匹配右边一个字符
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public ESQueryWrapper<T> likeRightOne(ESFunction<T, ?> column, Object val) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】like property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.LIKE_RIGHT_ONE).column(propertyName).value(val).build());
        return this;
    }


    /**
     * 升序
     *
     * @param column 字段
     * @return children
     */
    @Override
    public ESQueryWrapper<T> orderByAsc(ESFunction<T, ?> column) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】orderByAsc property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.ORDER_BY_ASC).column(propertyName).value(null).build());
        return this;
    }

    /**
     * 降序
     *
     * @param column 字段
     * @return children
     */
    @Override
    public ESQueryWrapper<T> orderByDesc(ESFunction<T, ?> column) {
        String propertyName = columnToString(column);
        log.debug("【wrapper】orderByDesc property name : {}", propertyName);
        conditions.add(Condition.builder().condition(Condition.ORDER_BY_DESC).column(propertyName).value(null).build());
        return this;
    }

    /**
     * 获取指定字段的资源
     *
     * @param columns 字段
     */
    @SafeVarargs
    @Override
    public final ESQueryWrapper<T> includeFields(ESFunction<T, ?>... columns) {
        List<String> fields = new ArrayList<>(columns.length);
        for (ESFunction<T, ?> column : columns) {
            fields.add(columnToString(column));
        }
        log.debug("【wrapper】includeFields properties : {}", fields);
        conditions.add(Condition.builder().condition(Condition.INCLUDE_FIELDS).includeColumns(fields).value(null).build());
        return this;
    }

    /**
     * 获取指定字段以外的字段资源
     *
     * @param columns 字段集
     */
    @SafeVarargs
    @Override
    public final ESQueryWrapper<T> excludeFields(ESFunction<T, ?>... columns) {
        List<String> fields = new ArrayList<>(columns.length);
        for (ESFunction<T, ?> column : columns) {
            fields.add(columnToString(column));
        }
        log.debug("【wrapper】excludeFields properties : {}", fields);
        conditions.add(Condition.builder().condition(Condition.EXCLUDE_FIELDS).excludeColumns(fields).value(null).build());
        return this;
    }

    /**
     * 根据方法引用获取字段名称
     */
    protected String columnToString(ESFunction<T, ?> column) {
        return getColumn(LambdaUtils.resolve(column));
    }

    /**
     * 获取 SerializedLambda 对应的列信息，从 lambda 表达式中推测实体类
     */
    private String getColumn(SerializedLambda lambda) {
        return PropertyNamer.methodToProperty(lambda.getImplMethodName());
    }


}
